package librecalcdataproxy.server;

import com.sun.star.beans.PropertyValue;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.comp.helper.BootstrapException;
import com.sun.star.container.XIndexAccess;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XController;
import com.sun.star.frame.XDispatchProviderInterception;
import com.sun.star.frame.XModel;
import com.sun.star.io.IOException;
import com.sun.star.lang.IllegalArgumentException;
import com.sun.star.lang.IndexOutOfBoundsException;
import com.sun.star.lang.WrappedTargetException;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.sheet.XCellRangeAddressable;
import com.sun.star.sheet.XSheetCellRanges;
import com.sun.star.sheet.XSpreadsheet;
import com.sun.star.sheet.XSpreadsheetDocument;
import com.sun.star.sheet.XSpreadsheets;
import com.sun.star.table.CellRangeAddress;
import com.sun.star.table.XCell;
import com.sun.star.uno.Exception;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import librecalcdataproxy.ui.ContextMenuInterceptor;
import librecalcdataproxy.ui.DispatchProviderInterceptor;

/**
 * @author Wiktor Toporek
 */
public final class DocInstance {
    private XSpreadsheetDocument doc = null;
    private XSpreadsheet curSheet = null;
    private short curSheetIndex;
    
    public EventExchanger eventExchanger;
    
    private Object oDesktop = null;
    private XComponentContext xContext;
    
    private DispatchProviderInterceptor dpi;
    private ContextMenuInterceptor cmi;

    
    public DocInstance(String documentURL) throws java.io.IOException, java.lang.Exception{
        try {
            // get the remote office component context
            xContext = Bootstrap.bootstrap();
        } catch (BootstrapException ex) {
            xContext = null;
        }
        if (xContext == null) 
            throw new NullPointerException("Could not bootstrap default Office.");
        
        XMultiComponentFactory xMCF = null;
        XComponentLoader xCLoader;
        XComponent xComp = null;

        xMCF = xContext.getServiceManager();
        try {
            // create a new instance of the the desktop
            oDesktop = xMCF.createInstanceWithContext(
                "com.sun.star.frame.Desktop", xContext );
        } catch (Exception ex) {
            throw new NullPointerException("Could not connect to desktop instance.");
        }

        xCLoader = ( XComponentLoader ) UnoRuntime.queryInterface(
            XComponentLoader.class, oDesktop );

        PropertyValue [] szEmptyArgs = new PropertyValue [0];
        try {
            xComp = xCLoader.loadComponentFromURL(documentURL, "_blank", 0, szEmptyArgs );
        } catch (IOException ex) {
            throw new java.io.IOException("Cannot open a file.");
        } catch (IllegalArgumentException ex) {
            throw new java.lang.Exception("Unknown error");
        }

        XSpreadsheetDocument libreDoc = (XSpreadsheetDocument) UnoRuntime.queryInterface(
            XSpreadsheetDocument.class, xComp);
        
        
        this.doc = libreDoc;
        selectSheet(0);
    }
    
    public boolean hookExportUI(){
        eventExchanger = new EventExchanger();
        
        XModel xDocModel = (XModel)UnoRuntime.queryInterface(XModel.class, doc );
        XController xController = xDocModel.getCurrentController();
        
        //Przechwytywanie wywoływanych komend
        dpi = new DispatchProviderInterceptor(xContext, eventExchanger);
        XDispatchProviderInterception xDispatchProvInterception = (XDispatchProviderInterception) UnoRuntime 
             .queryInterface(XDispatchProviderInterception.class, xController.getFrame());
        xDispatchProvInterception.registerDispatchProviderInterceptor(dpi);

        //Przechwytywanie menu kontekstowego (kliknięcie prawym klawiszem myszki w arkuszu)
        cmi = new ContextMenuInterceptor(xController, eventExchanger);
        return false;
    }

    public boolean selectSheet(int index) {
        try {
            XSpreadsheets xSheets = doc.getSheets() ;
            XIndexAccess oIndexSheets = (XIndexAccess) UnoRuntime.queryInterface(
                XIndexAccess.class, xSheets);
            curSheet = (XSpreadsheet) UnoRuntime.queryInterface(
                XSpreadsheet.class, oIndexSheets.getByIndex(0));
            curSheetIndex = (short)index;

        } catch (IndexOutOfBoundsException | WrappedTargetException e) {
            //System.out.println("Couldn't get Sheet " +e);
            //e.printStackTrace(System.err);
            return false;
        }
        return true;
    }
    
    //Funkcja pomocnicza wyciągająca obiekt komórki arkusza na podstawie podanych w argumentach wspołrzędnych
    public XCell getCell(int x, int y) throws java.lang.IndexOutOfBoundsException {
        XCell xCell = null;
        try{
            xCell = curSheet.getCellByPosition(x, y);
        }catch(com.sun.star.lang.IndexOutOfBoundsException ex){
            throw new java.lang.IndexOutOfBoundsException("Cell out of range");
        }
        return xCell;
    }
    
    //Funkcja pomocnicza pobierająca aktualne obszary zaznaczenia komórek w arkuszu
    public CellRangeAddress[] getCurrentSelection(){
        
        //Potrzebne kontenerowe obiekty
        XModel xDocModel = (XModel)UnoRuntime.queryInterface(XModel.class, doc );
        XController xController = xDocModel.getCurrentController();

        //Próbujemy pobrać interfejs zawierający współrzędne dla pojedyńczego prostokątnego zaznaczonego obszaru
        XCellRangeAddressable xcra = (XCellRangeAddressable)UnoRuntime.queryInterface(XCellRangeAddressable.class, xDocModel.getCurrentSelection());
        
        //W przypadku gdy powyższy interfejs jest osiągalny...
        if (xcra != null)
            return new CellRangeAddress[] {xcra.getRangeAddress()}; //...zwróć tablicę z adresami obszarów z jednym elementem pobieranym za pomocą jednej z metod tego interfejsu
        
        //Jeśli jednak poprzedni interfejs był nieosiągalny, próbujemy skorzystać z interfejsu obsługującego wiele zaznaczeń
        XSheetCellRanges xscr = (XSheetCellRanges)UnoRuntime.queryInterface(XSheetCellRanges.class, xDocModel.getCurrentSelection());
        if(xscr != null)
            return xscr.getRangeAddresses(); //Zwracamy wszystkie obszary zaznaczenia
        
        //Jeśli żaden z powyższych interfejsów jest prawidłowy, zwróć pustą tablice
        return new CellRangeAddress[]{};
    }
    
}
